import HashMap from "./HashMap";
import MagicValue from "./MagicValue";
import StringUtils from "./StringUtils";

export default class ObjectUtils {

    public static valueType(value: unknown): string {
        return Object.prototype.toString.call(value).slice(8, -1);
    }

    /*===================================================↓↓↓copy↓↓↓===================================================*/

    /**
     * 依据模型拷备数据
     *
     * @param source 数据源
     * @param target 目标源(携带模型信息：1.deepClone完全复制一个对象；2.copyProperties依据target模型拷备数据)
     * @param exclude 排除拷贝属性
     * @param isBlank 空值处理函数 返回false不添加这个元素值
     */
    public static copyProperties(source: any, target: any, exclude: string[] = [], isBlank?: any): void {
        if (source == null || target == null) {
            console.error("ObjectUtils copyProperties source or target can not be null!");
            return;
        }

        for (const key of Object.keys(target)) {
            if (!exclude.includes(key)) {
                if (source[key] && source[key] !== "") {
                    target[key] = source[key];
                } else if (isBlank !== undefined && isBlank()) {
                    target[key] = source[key];
                }
            }
        }
    }

    /**
     * 完全深拷备一个对象
     *
     * @param source 数据源
     * @param map 拷贝目标Map
     * @param exclude 排除拷贝属性
     * @param isBlank 空值处理函数 返回false不添加这个元素值
     */
    public static toMap<K, V>(source: any, map: HashMap<K, V>, exclude: string[] = [], isBlank?: any): void {
        if (source == null) {
            console.error("ObjectUtils toMap source can not be null!");
            return;
        }

        for (const key of Object.keys(source)) {
            if (!exclude.includes(key)) {
                if (source[key] && source[key] !== "") {
                    map.set(<K>key, source[key])
                } else if (isBlank !== undefined && isBlank()) {
                    map.set(<K>key, source[key])
                }
            }
        }
    }

    /**
     * 对象深拷贝
     *
     * @param data 数据对象
     * @param exclude 排除拷贝属性
     * @param isBlank 对象数据或对象中的空值处理函数 返回false不添加这个元素值 返回数据本身则添加这个元素
     * @param isElement data是否是对象内部的元素
     * @returns {*}
     */
    public static deepClone<T>(data: any = {}, exclude: string[] = [], isBlank?: any, isElement?: boolean): T {
        const type: string = ObjectUtils.getObjType(data);
        let obj: any;
        if (type === "array") {
            obj = [];
        } else if (type === "object") {
            obj = {};
        } else {
            if (isElement && (isBlank !== undefined)) {
                return isBlank(data);
            }
            return data;
        }
        if (type === "array") {
            for (let i = 0, len = data.length; i < len; i++) {
                obj.push(ObjectUtils.deepClone(data[i], exclude, isBlank, false));
            }
        } else if (type === "object") {
            for (const key in data) {
                if (Object.prototype.hasOwnProperty.call(data, key) && !exclude.includes(key)) {
                    const result = ObjectUtils.deepClone(data[key], exclude, isBlank, true);
                    if (result !== false) {
                        obj[key] = result;
                    }
                }
            }
        }
        return <T>obj;
    }

    private static readonly objectMap: { [key: string]: string } = {
        "[object Boolean]": "boolean",
        "[object Number]": "number",
        "[object String]": "string",
        "[object Function]": "function",
        "[object Array]": "array",
        "[object Date]": "date",
        "[object RegExp]": "regExp",
        "[object Undefined]": "undefined",
        "[object Null]": "null",
        "[object Object]": "object"
    };

    private static getObjType(obj: unknown) {
        if (obj instanceof Element) {
            return "element";
        }
        const toString = Object.prototype.toString;
        return ObjectUtils.objectMap[toString.call(obj)];
    }

    /*===================================================↑↑↑copy↑↑↑===================================================*/

    /**
     * 校验布尔值
     * @param element 布尔元素
     * @param boo 默认值
     * @returns {boolean}
     */
    public static validData(element: boolean, boo: boolean): boolean {
        return element ? element : boo;
    }

    /*==================================================↓↓↓convert↓↓↓==================================================*/

    /**
     * 二进制布尔值数组，最大支持数组长度为32。
     *
     * @param booleanArray 用来描述二进制集合的数字值
     * @param length ɪkˈspəʊnənt 2的n次方，这里是n的值，也是二进制集合的长度，即位运算模的最大长度。
     *
     * @return 返回值为true的布尔值在数组中的下标索引
     */
    public static parserBinaryBoolean(booleanArray: number, length: number = 32): number[] {
        if (length > 32) {
            return [];
        }

        const list: number[] = [];
        let index: number = 0;
        while (index < length) {
            if ((booleanArray & 1) === 1) {
                list.push(index);
            }
            booleanArray = booleanArray >>> 1;
            index++;
        }
        return list;
    }

    /**
     * 二进制布尔值数组，最大支持数组长度为32
     * @param booleanArray 二进制集合中布尔值为true的索引集合
     * @param length ɪkˈspəʊnənt 2的n次方，这里是n的值，也是二进制集合的长度，即位运算模的最大长度。
     *
     * @return 返回值为二进制布尔值数值表示形式
     */
    public static buildBinaryBoolean(booleanArray: number[], length: number = 32): number {
        if (length > 32) {
            return 0;
        }

        let list: number = 0;
        booleanArray.forEach((index: number) => {
            list += Math.pow(2, index);
        });
        return list;
    }

    /**
     * 格式化Table列数组类型
     * 数组结构：{ name: string, ... ... }[]
     * @param array
     * @param keyIndex 索引关键字，默认name
     */
    public static formatArrayColumn(array: { [name: string]: string }[], keyIndex: string = "name"): string {
        if (!array) {
            return MagicValue.EMPTY_STRING;
        }

        let names: string = "";
        for (const item of array) {
            names += item[keyIndex] + MagicValue.PAUSE_SYMBOL;
        }
        return names.substring(0, names.length - 1);
    }

    /**
     * 格式化Table列树类型
     * @param keys 数组数据(一般是id集合)
     * @param array 参考数组(一般包含id、name，树结构额外包含children)
     * @param props 映射(array的成员属性名称)
     */
    public static formatColumnArray(keys: string[], array: any[], props: {
        key: string,
        value: string,
        children: string
    }) {
        let result: string = MagicValue.EMPTY_STRING;
        for (const item of array) {
            if (keys.includes(item[props.key])) {
                result += item[props.value] + MagicValue.PAUSE_SYMBOL;
            }
            if (item[props.children]) {
                const str = ObjectUtils.formatColumnArray(keys, item.children, props);
                if (StringUtils.isNotBlank(str)) {
                    result += str + MagicValue.PAUSE_SYMBOL;
                }
            }
        }
        return result.substring(0, result.length - 1);
    }

    /*==================================================↑↑↑convert↑↑↑==================================================*/
}
